JAVA RedisTemplate实现(加锁/解锁) 解决高并发问题

您所在的位置:网站首页 并发 redis JAVA RedisTemplate实现(加锁/解锁) 解决高并发问题

JAVA RedisTemplate实现(加锁/解锁) 解决高并发问题

2024-03-22 21:07| 来源: 网络整理| 查看: 265

基于传统的单机模式下的并发锁,已远远不能满足当下高并发大负载的情况,当下常用的并发处理如下

       1、使用synchronized关键字

        2、select    for update   乐观锁

        3、使用redis实现同步锁

方案一 适合单机模式,

方案二 虽然满足多节点服务实例但 对变更操作的吞吐量有影响

方案三 基于redis nosql数据库  在效率与横向扩展方面都大大优于前两种方案

redis  单线程   在自身设计上一定程度可避免想成不安全  再者其效率高于关系型数据库

本次实现锁机制  基于redis 的两个指令 查询 redis.cn  网站  

指令一:SETNX key value

将key设置值为value,如果key不存在,这种情况下等同SET命令。 当key存在时,什么也不做。SETNX是”SET if Not eXists”的简写。

返回值

Integer reply, 特定值:

1 如果key被设置了0 如果key没有被设置  指令二:GETSET key value

自动将key对应到value并且返回原来key对应的value。如果key存在但是对应的value不是字符串,就返回错误。

返回值

bulk-string-reply: 返回之前的旧值,如果之前Key不存在将返回nil。

步骤一, pom文件

org.springframework.boot spring-boot-starter-data-redis org.apache.commons commons-pool2 2.6.0

步骤二,RedisCacheAutoConfiguration

package com.wh.whcloud.df.config; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; import com.wh.whcloud.core.redis.RedisUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; /** * Redis缓存配置 * * @author */ @Slf4j @Configuration @AutoConfigureAfter({RedisAutoConfiguration.class}) public class RedisCacheAutoConfiguration { /** * 重新配置一个RedisTemplate * * @param factory * @return */ @Bean public RedisTemplate redisTemplate(RedisConnectionFactory factory) { RedisTemplate template = new StringRedisTemplate(); template.setConnectionFactory(factory); Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(om); RedisSerializer stringSerializer = new StringRedisSerializer(); // key采用String的序列化方式 template.setKeySerializer(stringSerializer); // hash的key也采用String的序列化方式 template.setHashKeySerializer(stringSerializer); // value序列化方式采用jackson template.setValueSerializer(jackson2JsonRedisSerializer); // hash的value序列化方式采用jackson template.setHashValueSerializer(jackson2JsonRedisSerializer); template.setDefaultSerializer(jackson2JsonRedisSerializer); log.info("RedisTemplate配置 [{}]", template); return template; } @Bean @ConditionalOnMissingBean(RedisUtil.class) @ConditionalOnBean(RedisTemplate.class) public RedisUtil redisUtils(RedisTemplate redisTemplate) { RedisUtil redisUtil = new RedisUtil(redisTemplate); log.info("RedisUtil [{}]", redisUtil); return redisUtil; } }

步骤三, RedisLock 加解锁实现

package com.wh.whcloud.util; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.script.DefaultRedisScript; import org.springframework.data.redis.core.script.RedisScript; import java.util.Collections; import java.util.concurrent.TimeUnit; public class RedisLock { private static final Long SUCCESS = 1L; private long timeout = 9999; //获取锁的超时时间 /** * 加锁,无阻塞 * * @param * @param * @return */ public static Boolean tryLock(RedisTemplate redisTemplate, String key, String value, long expireTime) { try { //SET命令返回OK ,则证明获取锁成功 Boolean ret = redisTemplate.opsForValue().setIfAbsent(key, value, expireTime, TimeUnit.MILLISECONDS); return ret; } catch (Exception e) { e.printStackTrace(); return false; } return false; } /* Long start = System.currentTimeMillis(); for(;;){ //SET命令返回OK ,则证明获取锁成功 Boolean ret = redisTemplate.opsForValue().setIfAbsent(key, value, expireTime, TimeUnit.SECONDS); return ret; //否则循环等待,在timeout时间内仍未获取到锁,则获取失败 long end = System.currentTimeMillis() - start; if (end>=timeout) { return false; } }*/ /** * 解锁 * * @param * @param * @return */ public static Boolean unlock(RedisTemplate redisTemplate, String key, String value) { try { String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; RedisScript redisScript = new DefaultRedisScript(script, String.class); //redis脚本执行 //Object result = redisTemplate.execute(redisScript, Collections.singletonList(key), value)) Object result = redisTemplate.delete(Collections.singletonList(key)); if (SUCCESS.equals(result)) { return true; } } catch (Exception e) { e.printStackTrace(); return false; } return false; } }

注意:   

redisTemplate.execute 是redis脚本执行,可能报错, 具体参考: https://blog.csdn.net/cevery/article/details/108303919

测试:我这里自己创建两个 线程测试

@Slf4j @Component public class Schedule { @Resource private RedisTemplate redisTemplate; @Scheduled(cron = "0 0/1 * * * ? ") public void getDeviceStatus() throws InterruptedException { //启动两个线程,测试哪一个能够悠闲抢到Redis锁 Test1Runnable test1 = new Test1Runnable(); new Thread(test1).start(); Test2Runnable test2 = new Test2Runnable(); new Thread(test2).start(); } //自定义个唯一的Key值 public String key = "ZX123456789"; //保存数据方法 public boolean testSave(){ //获取锁 boolean isOK = RedisLock.tryLock(redisTemplate, key, "key+1", 2000); //处理业务 ,然后释放锁 if(isOK){ System.out.println("处理完业务,释放锁==="+RedisLock.unlock(redisTemplate, key, "key+1")); } return isOK; } class Test1Runnable implements Runnable { @Override public void run() { boolean result = testSave(); log.info(Thread.currentThread().getName()+"Test1获取锁:"+result); } } class Test2Runnable implements Runnable { @Override public void run() { boolean result = testSave(); log.info(Thread.currentThread().getName()+"Test2获取锁:"+result); } } }

效果:



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3